#include <iostream>
#include <queue>
#include <pthread.h>
/*
实现阻塞队列：
队列为空，那么消费者阻塞；队列满了，那么生产者阻塞
*/

using namespace std;

// 默认的阻塞队列容量
size_t gDftCap = 5;  

template <class T>
class BlockingQueue
{
public:
    BlockingQueue()
        : bq_()
        , cap_(gDftCap)
    {
        pthread_mutex_init(&mutex_, nullptr);
        pthread_cond_init(&csmcond_, nullptr);
        pthread_cond_init(&pdccond_, nullptr);
    }


    // 生产者生产
    void produce(const T& in)
    {
        // 加锁
        lockQueue();
        // 满了，那么生产者无法生产
        // 阻塞等待是函数，那么就可能调用失败，失败之后就会继续往下执行，被唤醒 != 满足条件
        // if (isFull())
        while (isFull())
        {   // 一定要保证阻塞被唤醒之后，也要具备被唤醒的条件
            pdcBlockwait(); // 阻塞等待
        }
        bq_.push(in);
        // 释放
        unlockQueue();
        // 唤醒消费者
        wakeupCsm(); // 这个步骤放在锁内还是锁外都是没问题的
    }

    // 消费者消费
    T custom()
    {
        // 加锁
        lockQueue();
        // 空的那么无法消费
        while (isEmpty())
        {
            // 消费者 阻塞等待
            csmBlockwait();
        }
        T ret = bq_.front();
        bq_.pop();
        // 释放
        unlockQueue();
        // 唤醒生产者
        wakeupPdc();
        return ret;
    }

    // 生产者阻塞等待
    // 这里的等待会进行锁的时候，所以这个接口的参数中自然也得有 mutex
    void pdcBlockwait()
    {
        // 进行阻塞等待，这个过程会进行锁的释放
        pthread_cond_wait(&pdccond_, &mutex_);
        // 当阻塞结束，返回的时候，pthread_cond_wait，会自动重新获取 mutex，然后才返回
    }

    // 消费者阻塞等待
    void csmBlockwait() 
    {
        pthread_cond_wait(&csmcond_, &mutex_);
    }

    // 唤醒生产者
    void wakeupPdc()
    {
        pthread_cond_signal(&pdccond_);   
    }
    
    // 唤醒生产者
    void wakeupCsm() 
    {
        pthread_cond_signal(&csmcond_);
    }

    ~BlockingQueue()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&csmcond_);
        pthread_cond_destroy(&pdccond_);
    }
private:
    // 加锁
    void lockQueue() 
    {
        pthread_mutex_lock(&mutex_);
    }

    void unlockQueue()
    {
        pthread_mutex_unlock(&mutex_);
    }

    bool isEmpty() 
    {
        return bq_.size() == 0;
    }

    bool isFull()
    {
        return bq_.size() == gDftCap;
    }

private:
    queue<T> bq_;  // 阻塞队列
    uint32_t cap_; // 容量
    // 互斥锁用来实现生产者和消费者的互斥
    pthread_mutex_t mutex_;  // 互斥锁
    // 条件变量用来实现生产者和消费者的同步
    pthread_cond_t csmcond_; // 消费者的条件变量
    pthread_cond_t pdccond_; // 生产者的条件变量
};